package com.andkrup.ds {
	import flash.events.IEventDispatcher;

	import com.andkrup.comm.subscribeLoadListener;	
	import com.andkrup.comm.unsubscribeLoadListener;	
	
	import flash.events.HTTPStatusEvent;	
	import flash.events.SecurityErrorEvent;	
	import flash.events.IOErrorEvent;	
	import flash.events.ProgressEvent;	
	import flash.events.Event;	
	
	import com.andkrup.comm.ILoadListener;	
	
	import flash.net.URLRequest;	
	import flash.net.URLLoader;	
	import flash.events.EventDispatcher;	
	
	import com.adobe.net.URI;	
	import com.andkrup.ds.IResourceLoader;
	import com.andkrup.ds.IQueue;
	
	/**
	 * @author Andkrup
	 * 
	 * A QueueLoader is required in situations where one external resource depends
	 * on other external resources are loaded beforehand.
	 * 
	 * An example could be that you have some external configuration data that,
	 * when parsed requires some external definitions to have been loaded prior.
	 * 
	 */
	public class QueueLoader extends EventDispatcher implements IResourceLoader, ILoadListener, IQueue {
		private static const STATE_READY:uint = 1;
		private static const STATE_LOADING:uint = 2;
		private static const STATE_ERROR:uint = 3;
		
		private static var _state : uint;
		
		[ArrayElementType("com.andkrup.ds.LoaderRequest")]
		private var queue : Array;
		private var cursor:uint;
		
		public var resumeOnError:Boolean;

		private static function get state():uint{
			return _state;
		}
		private static function set state(value:uint):void{
			_state = value;
		}
		
		public function QueueLoader(){
			queue = [];
			cursor = 0;
			resumeOnError = true;
		}
		
		public function add(uri:URI, listener:ILoadListener):LoaderRequest{
			var loader:URLLoader = new URLLoader();
			// subscribe listener to all loader.contentLoader event dispatches
			subscribeLoadListener(loader, listener);
			// subscribe this to all loader.contentLoader event dispatches
			subscribeLoadListener(loader, this);
			
			var lr:LoaderRequest = new LoaderRequest(loader, new URLRequest(), uri);
			queue.push(lr);
			next();
			return lr;
		}
		
		public function remove(loaderRequest:LoaderRequest, listener:ILoadListener):void{
			// unsubscribe listener to all loader.contentLoader event dispatches
			unsubscribeLoadListener(loaderRequest.loader as IEventDispatcher, listener);
		}
		/**
		 * NB! Remember to handle non-weakreferences to any loaders in the queue
		 */
		public function reset():void{
			var lr:LoaderRequest;
			if(queue){
				for each(lr in queue){
					unsubscribeLoadListener(lr.loader as IEventDispatcher, this);
				}
			}
			queue = [];
			cursor = 0;
		}
		
		public function next():void{
			if(state==STATE_READY){
				if(cursor>=0 && cursor<queue.length){
					load(queue[cursor] as LoaderRequest);
				}
			}
		}
		
		private function load(lr:LoaderRequest):void{
			if(lr){
				state = STATE_LOADING;
				lr.load();
			}
		}
		
		private function getLoaderRequestByLoader(loader:URLLoader):LoaderRequest{
			for each(var lr:LoaderRequest in queue){
				if(lr.loader==loader){
					return lr;
				}
			}
			return null;
		}
		
		public function onLoadComplete(e:Event):void{
			var l:URLLoader = e.target as URLLoader;
			if(l){
				var lr:LoaderRequest = getLoaderRequestByLoader(l);
				if(lr){
					state = STATE_READY;
					unsubscribeLoadListener(lr.loader as IEventDispatcher, this);
					cursor++;
					next();
				}
			}
		}
		public function onLoadOpen(e:Event):void{
		}
		public function onLoadInit(e:Event):void{
		}
		public function onLoadProgress(e:ProgressEvent):void{
		}
		public function onLoadIOError(e:IOErrorEvent):void{
			trace("QueueLoader:> onLoadIOError() "+e.text);
			if(resumeOnError){
				next();
			}
			else{
				state = STATE_ERROR;
			}
		}
		public function onLoadSecurityError(e:SecurityErrorEvent):void{
			trace("QueueLoader:> onLoadSecurityError() "+e.text);
			if(resumeOnError){
				next();
			}
			else{
				state = STATE_ERROR;
			}
		}
		public function onLoadHttpStatus(e:HTTPStatusEvent):void{
		}
	}
}
